package arch.chameleon.module.security.realm;

import java.io.Serializable;
import java.util.List;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.credential.PasswordService;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import arch.chameleon.module.exception.IncorrectCaptchaException;
import arch.chameleon.module.security.realm.model.EShiroUserStatus;
import arch.chameleon.module.security.realm.model.SRole;
import arch.chameleon.module.security.realm.model.SUser;
import arch.chameleon.module.security.realm.service.ShiroDbService;
import arch.chameleon.module.security.token.CaptchaUsernamePasswordToken;

import com.octo.captcha.service.CaptchaService;

public class ShiroDbRealm extends AuthorizingRealm {
	
	protected PasswordService passwordService;

	private transient Logger log = LoggerFactory.getLogger(this.getClass());
	
	/**
	 * 认证回调函数,登录时调用.
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
		CaptchaUsernamePasswordToken token = (CaptchaUsernamePasswordToken) authcToken;

		//验证码校验
		try{
			if(!captchaIgnore){
				boolean bCaptchaCorrect = captchaService.validateResponseForID(
					(String)SecurityUtils.getSubject().getSession().getId(), token.getCaptcha());		
			
				if(!bCaptchaCorrect){
					throw new IncorrectCaptchaException("验证码错误");
				}
			}else{//忽略验证码，供测试使用
				log.warn("ignore captcha for testing.");
			}
			
			
			SUser user = shiroDbService.findUserByLoginName(token.getUsername());
			if (user != null) {
				if (user.getStatus() == EShiroUserStatus.DISABLE) {
					throw new DisabledAccountException();
				}

				return new SimpleAuthenticationInfo(new ShiroUser(user.getLoginName(), user.getName()), 
						user.getPassword(), getName());
			} else {
				return null;
			}
		}catch(Exception e){
			throw new AuthenticationException(e.getMessage(), e);
		}
		
		
	}

	/**
	 * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用.
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		ShiroUser shiroUser = (ShiroUser) principals.fromRealm(getName()).iterator().next();
		List<SRole> lstRoles = shiroDbService.findRolesByLoginName(shiroUser.loginName);
		if (lstRoles != null) {
			SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
			for (SRole role : lstRoles) {
				//基于Role的权限信息
				info.addRole(role.getName());
			}
			return info;
		} else {
			return null;
		}
	}

	/**
	 * 更新用户授权信息缓存.
	 */
	public void clearCachedAuthorizationInfo(String principal) {
		SimplePrincipalCollection principals = new SimplePrincipalCollection(new ShiroUser(principal, null), getName());
		clearCachedAuthorizationInfo(principals);
	}

	private ShiroDbService shiroDbService;

	public void setShiroDbService(ShiroDbService shiroDbService) {
		this.shiroDbService = shiroDbService;
	}

	public static class HashPassword {
		public String salt;
		public String password;

		@Override
		public String toString() {
			return ReflectionToStringBuilder.toString(this, ToStringStyle.MULTI_LINE_STYLE);
		}
		
		
	}

	/**
	 * 自定义Authentication对象，使得Subject除了携带用户的登录名外还可以携带更多信息.
	 */
	public static class ShiroUser implements Serializable {

		private static final long serialVersionUID = 6572019817165264564L;
		
		public String loginName;
		public String name;

		public ShiroUser(String loginName, String name) {
			this.loginName = loginName;
			this.name = name;
		}

		public String getName() {
			return name;
		}

		public String getLoginName() {
			return loginName;
		}

		/**
		 * 本方法输出将作为默认的<shiro:principal/>输出.
		 */
		@Override
		public String toString() {
			return loginName;
		}

		/**
		 * 重载equals,只计算loginName;
		 */
		@Override
		public int hashCode() {
			return HashCodeBuilder.reflectionHashCode(this, "name");
		}

		/**
		 * 重载equals,只比较loginName
		 */
		@Override
		public boolean equals(Object obj) {
			return EqualsBuilder.reflectionEquals(this, obj, "name");
		}

	}
	
	private CaptchaService captchaService;
	
	private boolean captchaIgnore;

	public void setCaptchaService(CaptchaService captchaService) {
		this.captchaService = captchaService;
	}

	public void setCaptchaIgnore(boolean captchaIgnore) {
		this.captchaIgnore = captchaIgnore;
	}
}
